package odor;

import java.io.File;
import java.io.IOException;
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.FileReader;
import java.io.BufferedReader;
import java.util.ArrayList;
import java.util.Scanner;

import util.Util;
import util.SummedGaussian;

import main.Main;

public class Odor implements Comparable<Odor> 
{
	private double[] inputs;
	private SummedGaussian inputGaussian;
	private final String name;

	/**
	 * Create empty Odor
	 */
	public Odor()
	{
		name = "";
		
		inputGaussian = null;
		inputs = null;
	}
	
	/**
	 * Create odor with generalized aspects
	 * 
	 * @param numCols
	 * @param totalSum
	 * @param stdev
	 */
	public Odor(int numCols, double totalSum, double stdev) 
	{
		inputGaussian = new SummedGaussian(stdev, totalSum);

		inputs = inputGaussian.getNumRandomZScores(numCols)[1];
		
		// DEBUG
		if (Main.VERBOSE) 
		{
			for (double d : inputs) 
			{
				System.out.print(" " + d + " ");
			}
			System.out.println();
		}

		name = "";
	}
	
	/**
	 * Create Odor from Gaussian
	 * 
	 * @param numCols
	 * @param gauss
	 */
	public Odor(int numCols ,SummedGaussian gauss)
	{
		inputGaussian = gauss;
		
		inputs = inputGaussian.getNumRandomZScores(numCols)[1];
		
		name = "";
	}
	
	/**
	 * Create Odor with given inputs
	 * 
	 * @param inputs2: Array of inputs
	 */
	public Odor(double[] inputs2) 
	{
		this.inputGaussian = null;
		this.inputs = inputs2.clone();
		
		name = "";
	}

	public double getMean()
	{
		return (inputGaussian == null)? (Double.NaN):(inputGaussian.getMean());
	}

	public double getStandardDeviation() 
	{
		return (inputGaussian == null)? (Double.NaN):(inputGaussian.getStandardDeviation());
	}

	public static Odor parseStringToOdor(String line)
	{
		Odor odor = null;
		java.util.Scanner sc = new java.util.Scanner(line);
		
		
		
		return odor;
	}

	/**
	 * TODO Generates a randomized odor battery w/ specified number of Odors
	 * 
	 * @param numOdors
	 *            - number of odors in battery
	 * @param numInputCols
	 *            - number of input columns
	 * @param totalInputSum
	 *            - total input sum of each odor
	 * @return an Odor array containing the battery
	 * @deprecated
	 */
	public static Odor[] generateOdorBattery(int numOdors, int numInputCols,
			int totalInputSum) 
	{
		Odor[] list = new Odor[numOdors];
		
		

		return list;
	}

	/**
	 * Generates a randomized odor battery w/ specified number of Odors
	 * 
	 * @param numOdors
	 *            - number of odors in battery
	 * @param numInputCols
	 *            - number of input columns
	 * @param totalInputSum
	 *            - total input sum of each odor
	 * @param sigma
	 *            - the standard deviation of each odor
	 * @return an Odor array containing the battery
	 */
	public static Odor[] generateOdorBattery(int numOdors, int numInputCols,
			double totalInputSum, double sigma) 
	{
		Odor[] list = new Odor[numOdors];
		
		for (int x = 0; x < list.length; x++) 
		{
			Odor od = new Odor(numInputCols, totalInputSum, sigma);
			list[x] = od;
		}

		return list;
	}

	/**
	 * TODO Parse file data into Odor battery array
	 * 
	 * @deprecated
	 * @param f
	 * @return
	 */
	public static Odor[] fileToBattery(File f) 
	{
		Scanner sc = null;
		Odor[] battery = null;

		try 
		{
			sc = new Scanner(f);
			ArrayList<Odor> list = new ArrayList<Odor>();
			
			while (sc.hasNextLine()) 
			{
				double[] inputs = Util.stringArrayToDouble(sc.nextLine().split(","));
				list.add(new Odor(inputs));
				
				// DEBUG
				if (Main.VERBOSE) 
				{
					System.out.println();
					System.out.print("Input Array: ");
					Util.printArray(inputs);
					System.out.println();
				}
			}

			battery = new Odor[list.size()];
			battery = list.toArray(battery);
		}
		catch (IOException ex) 
		{
			ex.printStackTrace();
		} 
		finally 
		{
			if (sc != null) sc.close();
		}

		return battery;

	}

	/**
	 * TODO Parse Odor battery array into file data
	 * 
	 * @deprecated
	 * @param battery
	 * @param f
	 * @throws IOException
	 */
	public static void writeBatteryToFile(Odor[] battery, File f)
			throws IOException 
	{
		if (f.isFile() && f.canWrite()) 
		{
			if (!f.exists()) 
			{
				f.createNewFile();
			}

			BufferedWriter bw = new BufferedWriter(new FileWriter(f));

			for (Odor od : battery) 
			{
				bw.write(od.toWritable());
				bw.newLine();
			}

			bw.close();
			
		} 
		else
			throw new java.io.FileNotFoundException();
	}

	@Override
	public String toString() 
	{
		if (this.name == null || this.name.compareTo("") == 0)
			return super.toString();
		else
			return this.name;
	}

	/**
	 * Produces String-version, CSV-style writable file string representation
	 * 
	 * @return Single String line of CSV input values
	 */
	public String toWritable() 
	{
		String output = "";

		for (int x = 0; x < this.inputs.length; x++) 
		{
			output = output + inputs[x] + ",";
		}

		output = output + ",";

		return output;
	}

	/**
	 * Protected (safe) cloned array of input values
	 * 
	 * @return cloned input array
	 */
	public double[] getInputs() 
	{
		double[] clonedInputs = new double[inputs.length];

		for (int x = 0; x < inputs.length; x++) 
		{
			clonedInputs[x] = inputs[x];
		}

		return clonedInputs;
	}

	/**
	 * Two Odors are equal IFF their respective Gaussians are equal
	 * 
	 * NOTE: Equals and compareTo relate to entirely different qualities
	 */
	@Override
	public boolean equals(Object o) 
	{
		Odor o2 = (o instanceof Odor) ? ((Odor)o):null;
		
		if (o2 == null) return false;
		else return (o2.inputGaussian.equals(this.inputGaussian));
	}

	/**
	 * Compares two odors as vectors using dot-product
	 * 
	 * NOTE: Equals and compareTo relate to entirely different qualities
	 * 
	 * @return Integer.MIN_VALUE if odors have different sizes
	 *  OR 0 if both odors have the same inputs
	 *  OR the dot product of the odor's inputs
	 */
	public int compareTo(Odor arg0) 
	{
		if (this.inputs.length > arg0.inputs.length || this.inputs.length < arg0.inputs.length)
			return Integer.MIN_VALUE;
		else 
			return (int)Util.dotProduct(this.inputs, arg0.inputs);
	}
}
